#!/bin/sh

#
#    Copyright 2014-2017 Nest Labs Inc. All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License");
#    you may not use this file except in compliance with the License.
#    You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.
#

#
#    Description:
#      This file is a convenience script for building, for the current
#      build host system, the minimal, core set of GNU autotools on
#      which other projects' build systems depend.
#

EXTENSIONS="tar.gz tar.xz"

VERBOSE=0

stem=tools/packages
abs_srcdir=`pwd`
abs_top_srcdir=`echo ${abs_srcdir} | sed -e s,/${stem},,g`

abs_top_hostdir="${abs_top_srcdir}/tools/host"

#
# usage
#
# Display program usage.
#
usage() {
    name=`basename ${0}`

    echo "Usage: ${name} [ options ] [ -- <package> ... ]"

    if [ $1 -ne 0 ]; then
        echo "Try '${name} -h' for more information."
    fi

    if [ $1 -ne 1 ]; then
        echo ""
	echo "  --arch ARCH      Build the package for ARCH host architecture."
        echo "  -h, --help       Print this help, then exit."
        echo "  --builddir DIR   Build the packages in DIR."
        echo "  --destdir DIR    Install the built tools to directory DIR."
        echo "  --srcdir DIR     Find package, version, and URL metadata in DIR."
        echo "  -v, --verbose    Enable verbose output."
        echo ""
    fi

    exit $1
}

#
# error <...>
#
# Display to standard error the specified arguments
#
error() {
    echo "${*}" >&2
}

#
# verbose <...>
#
# Display to standard error the specified arguments
#
verbose() {
    echo "${*}" >&2
}

#
# separator
#
# Display to standard error a log separator.
#
separator() {
    verbose "--------------------------------------------------------------------------------"
}

#
# trailer
#
# Display to standard error a log trailer.
#
trailer() {
    verbose "================================================================================"
}

#
# banner <message ...>
#
# Display to standard error a log banner with a message.
#
banner() {
    trailer

    verbose "${*}"
}

#
# subbanner <message ...>
#
# Display to standard error a log subbanner with a message.
#
subbanner() {
    separator

    verbose "${*}"
}

#
# find_archive <subdir> <package> <extensions...>
#
# Attempt to find the specified package archive in the provided
# subdirectory matching one of the specified extensions.
#
find_archive() {
    local subdir="${1}"
    local package="${2}"
    local archive

    shift 2

    for extension in ${*}; do
        local maybe_archive="${subdir}/${package}.${extension}"

        verbose "  FIND     ${maybe_archive}"

        if [ -r "${maybe_archive}" ]; then
            archive="${maybe_archive}"
            break
        fi
    done

    echo "${archive}"
}

#
# find_versioned_or_nonversioned_archive <subdir> <package> <version> <extensions...>
#
# Attempt to find the specified version-qualified or non-qualified
# package archive in the provided subdirectory matching one of the
# specified extensions.
#
find_versioned_or_nonverioned_archive() {
    local subdir="${1}"
    local package="${2}"
    local version="${3}"
    local fqpackage="${package}-${version}"
    local archive

    shift 3

    archive="`find_archive ${subdir} ${fqpackage} ${*}`"

    if [ -z "${archive}" ]; then
        archive="`find_archive ${subdir} ${package} ${*}`"
    fi

    echo "${archive}"
}

#
# fetch_url_with_command <fetchdir> <url> <command ...>
#
# Attempt to fetch the specified URL to the provided directory with
# the provided command.
#
fetch_url_with_command() {
    local fetchdir="${1}"
    local url="${2}"
    local curdir="`pwd`"

    shift 2

    cd "${fetchdir}"

        ${*} "${url}"

        if [ ${?} -ne 0 ]; then
            error "Failed to fetch ${url} with ${1}."
        fi

    cd "${curdir}"
}

#
# fetch_url <fetchdir> <url>
#
# Attempt to fetch the specified URL to the provided directory with
# either wget or curl.
#
fetch_url() {
    local fetchdir="${1}"
    local url="${2}"

    # Try to fetch the package using wget or curl

    fetch_url_with_command "${fetchdir}" "${url}" wget --quiet ||
        fetch_url_with_command "${fetchdir}" "${url}" curl --silent --remote-name
}

#
# fetch_package <finddir> <fetchdir> <package> <version> <url>
#
# Attempt to fetch the specified package version to <fetchdir>, if a
# local archive does not already exist in <finddir> or <fetchdir>,
# from the provided URL.
#
fetch_package() {
    local finddir="${1}"
    local fetchdir="${2}"
    local package="${3}"
    local version="${4}"
    local url="${5}"
    local fqpackage="${package}-${version}"

    # Check whether a local archive already exists in <finddir> or <fetchdir>.

    archive="`find_versioned_or_nonverioned_archive ${finddir} ${package} ${version} ${EXTENSIONS}`"

    if [ -z "${archive}" ]; then
        archive="`find_versioned_or_nonverioned_archive ${fetchdir} ${package} ${version} ${EXTENSIONS}`"
    fi

    # If no archive was found, attempt to fetch it.

    if [ -z "${archive}" ]; then
        verbose "  FETCH    ${url}"

        fetch_url "${fetchdir}" "${url}"

    else
        error "No, found ${archive} locally."

    fi
}

#
# removetmp
#
# Remove temporary files and directories used during the run of this
# script.
#
removetmp() {
    rm -f "${LIBTOOLIZE}"
    if [ -n "${AUTOM4TE_CFG}" ]; then
        rm -f "${AUTOM4TE_CFG}"
    fi
}

#
# build_package <package> <version> <host> <archivedir> <builddir> <destdir>
#
# Build the specified package version for <host> in <builddir> from
# the archive found in <archivedir> and install it into the specified
# destination directory, <destdir>.
#
build_package() {
    local package="${1}"
    local version="${2}"
    local host="${3}"
    local archivedir="${4}"
    local builddir="${5}"
    local destdir="${6}"
    local fqpackage="${package}-${version}"
    local curdir=`pwd`
    local archive

    verbose "  CHECK    ${package}"

    archive="`find_versioned_or_nonverioned_archive ${archivedir} ${package} ${version} ${EXTENSIONS}`"

    if [ -z "${archive}" ]; then
        error "Could not find an archive for ${package}."
        exit 1
    fi

    if [ ! -d "${archivedir}/${fqpackage}" ]; then
	verbose "  TAR      ${archive}"
        tar --directory "${archivedir}" -xf "${archive}" || exit ${?}
    fi

    # If possible, attempt to be self-sufficient, relying on GNU autotools
    # executables installed along with the SDK itself.

    if [ -d "${destdir}/${host}/bin" ]; then
        export PATH="${destdir}/${host}/bin:${PATH}"
    fi

    if [ -d "${destdir}/bin" ]; then
        export PATH="${destdir}/bin:${PATH}"
    fi

    export ACLOCAL=`which aclocal`
    export AUTOCONF="`which autoconf`"
    export AUTOHEADER="`which autoheader`"
    export AUTOM4TE="`which autom4te`"
    export AUTOMAKE="`which automake`"
    export LIBTOOLIZE="`which libtoolize || which glibtoolize`"
    export M4=`which m4`

    # Establish, if necessary, some SDK-specific directories needed to
    # override various paths in GNU autotools that otherwise expect to be
    # absolute (e.g. /usr/share, etc.).

    if [ -d "${destdir}/share" ]; then
        if [ -d "${destdir}/share/autoconf" ]; then
            export AC_MACRODIR="${destdir}/share/autoconf"

            export autom4te_perllibdir="${destdir}/share/autoconf"
	fi

        if [ -d "${destdir}/share/automake-1.14" ]; then
            export PERL5LIB="${destdir}/share/automake-1.14:${PERL5LIB}"
        fi
    fi

    trap "removetmp" 1 2 3 9 15

    #
    # Generate any temporary files that need to be patched at run time
    # with the location of the SDK tree, including:
    #
    #   -  The autom4te configuration file
    #   -  The libtoolize executable script
    #

    if [ -r "${destdir}/share/autoconf/autom4te.cfg" ]; then
        export AUTOM4TE_CFG="${destdir}/autom4te.cfg"

        sed -e "s,//share/autoconf,${destdir}/share/autoconf,g" < "${destdir}/share/autoconf/autom4te.cfg" > "${AUTOM4TE_CFG}"
    fi

    if [ -r "${destdir}/${host}/bin/libtoolize" ]; then
        export LIBTOOLIZE="${destdir}/libtoolize"

        sed -e "s,//share/libtool,${destdir}/share/libtool,g" -e "s,//share/aclocal,${destdir}/share/aclocal,g" < "${destdir}/${host}/bin/libtoolize" > "${LIBTOOLIZE}"

        chmod 775 "${LIBTOOLIZE}"
    fi

    # Configure the package

    if [ ${VERBOSE} -gt 0 ]; then
        CONFIGURE_FLAGS=""
    else
        CONFIGURE_FLAGS="--quiet"
    fi

    verbose "  CONFIG   ${package}"

    cd "${builddir}"

        ${archivedir}/${fqpackage}/configure ${CONFIGURE_FLAGS} --prefix=/ --exec-prefix=/${host} || exit ${?}

    cd "${curdir}"

    # Build the package

    verbose "  MAKE     ${package}"

    make V=${VERBOSE} -C "${builddir}" all || exit ${?}

    # Install / stage the package

    verbose "  INSTALL  ${package}"

    make V=${VERBOSE} -C "${builddir}" DESTDIR="${destdir}" install-strip || exit ${?}

    # Remove any temporary files created.

    removetmp
}

# Parse out any command line options

while [ ${#} -gt 0 ]; do
    case ${1} in
	--arch)
            ARCH=${2}
            shift 2
            ;;

	-h|--help)
	    usage 0
	    ;;

	--builddir)
	    BUILDDIR=${2}
	    shift 2
	    ;;

	--destdir)
	    DESTDIR=${2}
	    shift 2
	    ;;

        --srcdir)
            SRCDIR=${2}
            shift 2
	    ;;

        -v|--verbose)
            $((VERBOSE++))
            shift 1
	    ;;

	--)
	    shift 1
	    break
	    ;;

	*)
          usage 1
	  ;;
    esac
done

# If the architecture is not specified, fail.

if [ -z "${ARCH}" ]; then
    error "The host architecture was not specified via --arch."

    usage 1
fi

# If the --builddir option wasn't specified, then provide a default.

if [ -z "${BUILDDIR}" ]; then
    BUILDDIR="`pwd`"
fi

# If the --destdir option wasn't specified, then provide a default.

if [ -z "${DESTDIR}" ]; then
    DESTDIR=${abs_top_hostdir}
fi

# If the --srcdir option wasn't specified, then provide a default.

if [ -z "${SRCDIR}" ]; then
    SRCDIR="`pwd`"
fi

# Determine what packages to build

if [ ${#} -gt 0 ]; then
    PACKAGES="${*}"

else
    PACKAGES="`cat ${SRCDIR}/packages`"

fi

# Build each package

banner "Building GNU autotools for ${ARCH}..."

for package in ${PACKAGES}; do
    url="`cat ${SRCDIR}/${package}/${package}.url`"
    version="`cat ${SRCDIR}/${package}/${package}.version`"

    # Fetch, if necessary, the package from the canonical source location.

    subbanner "  CHECK    ${package}"

    fetch_package "${SRCDIR}/${package}" "${BUILDDIR}" "${package}" "${version}" "${url}"

    # Build and install the package.

    verbose "  MKDIR    ${BUILDDIR}/build/${ARCH}/${package}-${version}"

    mkdir -p "${BUILDDIR}/build/${ARCH}/${package}-${version}" || exit ${?}

    build_package "${package}" "${version}" "${ARCH}" "${BUILDDIR}" "${BUILDDIR}/build/${ARCH}/${package}-${version}" "${DESTDIR}"
done

trailer
